//  This file was generated with SpriteHelper
//  http://www.spritehelper.org
//
//  SpriteHelperLoader.mm - online version
//  Created by Bogdan Vladu
//  Copyright 2011 Bogdan Vladu. All rights reserved.
//
////////////////////////////////////////////////////////////////////////////////
//
//  This software is provided 'as-is', without any express or implied
//  warranty.  In no event will the authors be held liable for any damages
//  arising from the use of this software.
//  Permission is granted to anyone to use this software for any purpose,
//  including commercial applications, and to alter it and redistribute it
//  freely, subject to the following restrictions:
//  The origin of this software must not be misrepresented; you must not
//  claim that you wrote the original software. If you use this software
//  in a product, an acknowledgment in the product documentation would be
//  appreciated but is not required.
//  Altered source versions must be plainly marked as such, and must not be
//  misrepresented as being the original software.
//  This notice may not be removed or altered from any source distribution.
//  By "software" the author refers to this code file and not the application 
//  that was used to generate this file.
//
////////////////////////////////////////////////////////////////////////////////

#include "SpriteHelperLoader.h"
#include "iostream"
#include "assert.h"
#include "fstream"
#include <sstream>
#include <map>
#include "cocoa/CCNS.h"
////////////////////////////////////////////////////////////////////////////////
////////////////SPECIAL CLASSES FOR COCOS2D-X TO READ FROM A PLIST FILE/////////
////////////////////////////////////////////////////////////////////////////////
class SpriteInfo
{
public:
	SpriteInfo(){};
	~SpriteInfo(){properties.clear(); fixtures.clear();};
	
	LHDictionary properties;
	std::vector<std::vector<std::string> > fixtures;
};
////////////////////////////////////////////////////////////////////////////////
/// converts degrees to radians
#define DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) / 180.0f * (float)M_PI)
/// converts radians to degrees
#define RADIANS_TO_DEGREES(__ANGLE__) ((__ANGLE__) / (float)M_PI * 180.0f)


string concatenate(std::string const& str, int i)
{
    stringstream s;
    s << str << i;
    return s.str();
}
////////////////////////////////////////////////////////////////////////////////

SpriteHelperLoader::SpriteHelperLoader(const std::string& levelFile)
{
	loadSpriteHelperSceneFile(levelFile);
}

////////////////////////////////////////////////////////////////////////////////

void SpriteHelperLoader::assertMessage(bool value, const std::string message)
{
	if(value == false)
	{
		CCLog(message.c_str());
	}
	
	assert(value);
}
////////////////////////////////////////////////////////////////////////////////

CCSprite* SpriteHelperLoader::spriteWithUniqueName(const std::string &name, 
                               const CCPoint& position, 
                               CCLayer* layer)
{
    for(size_t i = 0; i < lhSprites.size(); ++i)
	{
		SpriteInfo* sprite = lhSprites[i];
        
		if(sprite->properties["Name"] == name)
		{
			CCSprite* ccsprite = spriteFromMap(sprite->properties);
            ccsprite->setPosition(position);
            
			if(0 != ccsprite && 0 != layer)
				layer->addChild(ccsprite, atoi(sprite->properties["ZOrder"].c_str()));
			
			return ccsprite;
		}
	}
	return 0;
}

////////////////////////////////////////////////////////////////////////////////

CCSprite* SpriteHelperLoader::spriteWithUniqueName(const std::string& name, 
                               const CCPoint& position)
{
    return spriteWithUniqueName(name, position, 0);
}
////////////////////////////////////////////////////////////////////////////////

CCSprite* SpriteHelperLoader::spriteWithUniqueName(const std::string& name)
{
    return spriteWithUniqueName(name, ccp(0.0f, 0.0f), 0);
}

////////////////////////////////////////////////////////////////////////////////

CCSprite* SpriteHelperLoader::animSpriteWithUniqueName(const std::vector<std::string>& framesNames,
                                   const CCPoint& position,
                                   CCLayer* layer,
                                   const float& delay,
                                   const int& noOfRepeats,
                                   CCAction** animAction)
{
	
    if(framesNames.size() == 0)
        return 0;
    
    LHDictionary* spriteProperties = propertyForName(framesNames[0]);
    
    if(0 != spriteProperties)
    {
        CCSprite* spr = spriteFromMap(*spriteProperties);
        spr->setPosition(position);
        
        CCSpriteFrame* frame1 = CCSpriteFrame::frameWithTexture(spr->getTexture(), 
                                                                CCRectFromString( (const char *)(((*spriteProperties)["Frame"]).c_str())) );
        
        CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFrame(frame1, framesNames[0].c_str());
        
        CCMutableArray<CCSpriteFrame*> frames;
        frames.addObject(frame1);
        
        for(int i = 1; i < framesNames.size(); ++i)
        {
            LHDictionary* frameSprite = propertyForName(framesNames[i]);
            
            CCSpriteFrame* frame = CCSpriteFrame::frameWithTexture(spr->getTexture(), CCRectFromString( (const char *)(((*frameSprite)["Frame"]).c_str())));
            
            CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFrame(frame, framesNames[i].c_str());
            
            frames.addObject(frame);
        }
        
        
        CCAnimation *anim = CCAnimation::animationWithFrames(&frames, delay);
        
        CCAction* animAct; 
        if(noOfRepeats != 0)
            animAct = CCRepeat::actionWithAction(CCAnimate::actionWithAnimation(anim, false), noOfRepeats);
        else
            animAct = CCRepeatForever::actionWithAction(CCAnimate::actionWithAnimation(anim, false));

        
        spr->runAction(animAct);
        
        if(animAction != 0)
            *animAction = animAct;
        
        if(0 != layer)
            layer->addChild(spr, atoi((*spriteProperties)["ZOrder"].c_str()));
        return spr;
    }
    
    return 0;
    

}

CCSprite* SpriteHelperLoader::animSpriteWithUniqueName(const std::string& name,
                                   const CCPoint& position,
                                   CCLayer* layer,
                                   const int& frameNo,
                                   const float& delay,
                                   const int& noOfRepeats,
                                   CCAction** animAction)
{
    std::vector<std::string> frameNames;
    for(int i = 1; i < frameNo +1; ++i)
    {
        std::string frame(concatenate(name, i));

        frameNames.push_back(frame);
    }
    
    return animSpriteWithUniqueName(frameNames,
                                    position,
                                    layer,
                                    delay,
                                    noOfRepeats,
                                    animAction);
    
}

CCSprite* SpriteHelperLoader::animSpriteWithUniqueName(const std::string& name,
                                   const int& startFrameNo,
                                   const int& endFrameNo,
                                   const CCPoint& position,
                                   CCLayer* layer,
                                   const float& delay,
                                   const int& noOfRepeats,
                                   CCAction** animAction)
{
    std::vector<std::string> frameNames;
    for(int i = startFrameNo; i < endFrameNo; ++i)
    {
        std::string frame(concatenate(name, i));
        
        frameNames.push_back(frame);
    }
    
    return animSpriteWithUniqueName(frameNames,
                                    position,
                                    layer,
                                    delay,
                                    noOfRepeats,
                                    animAction);
    
}

////////////////////////////////////////////////////////////////////////////////

CCAction* SpriteHelperLoader::actionForFrames(const std::vector<std::string>& frameNames, 
                                              const float& delay)
{
    if(frameNames.size()== 0)
        return 0;
    
    LHDictionary* spriteProperties = propertyForName(frameNames[0]);
    
    if(0 != spriteProperties)
    {
        CCSprite* spr = spriteFromMap(*spriteProperties);
        
        CCSpriteFrame* frame1 = CCSpriteFrame::frameWithTexture(spr->getTexture(), 
                                                                CCRectFromString( (const char *)((*spriteProperties)["Frame"].c_str())));
        
        CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFrame(frame1, frameNames[0].c_str());
        
        CCMutableArray<CCSpriteFrame*> frames;
        frames.addObject(frame1);
        
        for(int i = 1; i < frameNames.size(); ++i)
        {
            LHDictionary* frameSprite = propertyForName(frameNames[i]);
            
            CCSpriteFrame* frame = CCSpriteFrame::frameWithTexture(spr->getTexture(), CCRectFromString( (const char *)((*frameSprite)["Frame"].c_str())));
            
            CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFrame(frame, frameNames[i].c_str());
            
            frames.addObject(frame);
        }
        
        
        CCAnimation *anim = CCAnimation::animationWithFrames(&frames, delay);
        
        CCAction* animAct = CCRepeatForever::actionWithAction(CCAnimate::actionWithAnimation(anim, false));
        
        delete spr;
        return animAct;
    }
    
    return 0;
}

CCAction* SpriteHelperLoader::actionForFrames(const std::string& name, 
                                              const int& frameNo, 
                                              const float& delay)
{
    std::vector<std::string> frameNames;
    for(int i = 1; i < frameNo +1; ++i)
    {
        std::string frame(concatenate(name, i));
        
        frameNames.push_back(frame);
    }

    return actionForFrames(frameNames, delay);
}

CCAction* SpriteHelperLoader::actionForFrames(const std::string& name, 
                                              const int& startFrameNo,
                                              const int& endFrameNo,
                                              const float& delay)
{
    std::vector<std::string> frameNames;
    for(int i = startFrameNo; i < endFrameNo; ++i)
    {
        std::string frame(concatenate(name, i));
        
        frameNames.push_back(frame);
    }
    return actionForFrames(frameNames, delay);
}


////////////////////////////////////////////////////////////////////////////////
b2Body* SpriteHelperLoader::bodyWithUniqueName(const std::string& uniqueName, 
                                               const CCPoint& position,
                                               CCLayer* layer,
                                               b2World* world)
{
	for(size_t i = 0; i < lhSprites.size(); ++i)
	{
		SpriteInfo* sprite = lhSprites[i];
		
		if(sprite->properties["Name"] == uniqueName)
		{
			CCSprite* ccsprite = spriteFromMap(sprite->properties);
            ccsprite->setPosition(position);
			if(0 != ccsprite)
			{
                if(0 != layer)
                    layer->addChild(ccsprite, atoi(sprite->properties["ZOrder"].c_str()));
				
				return b2BodyFromMap(sprite->properties, 
									 sprite->fixtures,
									 ccsprite,
									 world);
			}
		}
	}
	
	return 0;
}


b2Body* SpriteHelperLoader::animBodyWithUniqueName(const std::string& name,
                                                   CCSprite* animSprite,
                                                   b2World* world)
{
    
    if(0 == animSprite)
        return 0;
    
    SpriteInfo* sprInfo = spriteInfoForName(name);
    
    if(0 != sprInfo)
    {       
        return b2BodyFromMap(sprInfo->properties, 
                             sprInfo->fixtures,
                             animSprite,
                             world);
    }
    
	return 0;
}

////////////////////////////////////////////////////////////////////////////////
SpriteHelperLoader::~SpriteHelperLoader()
{		
	for(int i = 0; i < (int)lhSprites.size(); ++i){
		delete lhSprites[i];
	}
	lhSprites.clear();
}
///////////////////////////PRIVATE METHODS//////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
LHDictionary* SpriteHelperLoader::propertyForName(const std::string& name)
{
    for(size_t i = 0; i < lhSprites.size(); ++i)
	{
		SpriteInfo* sprite = lhSprites[i];
        
		if(sprite->properties["Name"] == name)
		{
            return &lhSprites[i]->properties;
        }
    }
    return 0;
}
////////////////////////////////////////////////////////////////////////////////
SpriteInfo* SpriteHelperLoader::spriteInfoForName(const std::string& name)
{
    for(size_t i = 0; i < lhSprites.size(); ++i)
	{
		SpriteInfo* sprite = lhSprites[i];
        
		if(sprite->properties["Name"] == name)
		{
            return sprite;
        }
    }
    return 0;
}
////////////////////////////////////////////////////////////////////////////////
CCSprite* SpriteHelperLoader::spriteFromMap(LHDictionary &spriteMap)
{
	CCSprite *ccsprite = CCSprite::spriteWithFile(spriteMap["Image"].c_str(), 
												  CCRectFromString( (const char *)(spriteMap["Frame"].c_str())));
	setSpritePropertiesFromMap(ccsprite, spriteMap);
												  
	return ccsprite;
}
////////////////////////////////////////////////////////////////////////////////
void SpriteHelperLoader::setSpritePropertiesFromMap(CCSprite* ccsprite, 
												   LHDictionary &spriteMap)
{
	//convert position from LH to Cocos2d coordinates
	ccsprite->setOpacity((GLubyte)(255*atof(spriteMap["Opacity"].c_str())));
	CCRect color = CCRectFromString( (const char *)(spriteMap["Color"].c_str()));
	ccsprite->setColor(ccc3(255*color.origin.x, 255*color.origin.y, 255*color.size.width));
	CCPoint scale = cocos2d::CCPointFromString(spriteMap["Scale"].c_str());
	ccsprite->setScaleX(scale.x);
	ccsprite->setScaleY(scale.y);
}
////////////////////////////////////////////////////////////////////////////////
void SpriteHelperLoader::setFixtureDefPropertiesFromMap(std::map<std::string, std::string>& spritePhysic,
													   b2FixtureDef*shapeDef)
{
	shapeDef->density = atof(spritePhysic["Density"].c_str());
	shapeDef->friction = atof(spritePhysic["Friction"].c_str());
	shapeDef->restitution = atof(spritePhysic["Restitution"].c_str());
	shapeDef->isSensor = (bool)atoi(spritePhysic["IsSenzor"].c_str());
	
	shapeDef->filter.categoryBits = atoi(spritePhysic["Category"].c_str());
	shapeDef->filter.maskBits = atoi(spritePhysic["Mask"].c_str());
	shapeDef->filter.groupIndex = atoi(spritePhysic["Group"].c_str());
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
b2Body* SpriteHelperLoader::b2BodyFromMap(LHDictionary& properties,
										 std::vector<std::vector<std::string> >&fixtures,
										 CCSprite* ccsprite,
										 b2World* world)
{
	b2BodyDef bodyDef;	
	
	b2Vec2 position = b2Vec2(ccsprite->getPosition().x/PTM_RATIO,ccsprite->getPosition().y/PTM_RATIO);
	
    bodyDef.type = (b2BodyType)atoi(properties["PhysicType"].c_str());
	bodyDef.position = position;
	
	bodyDef.userData = ccsprite;
	b2Body* body = world->CreateBody(&bodyDef);
	
	body->SetFixedRotation((bool)atoi(properties["IsFixedRot"].c_str()));
	
	CCPoint scale = cocos2d::CCPointFromString(properties["Scale"].c_str()); 
    CCRect frame = CCRectFromString( (const char *)(properties["Frame"].c_str()));
    CCSize size = frame.size;
    
	if(fixtures.size() == 0)
	{
        int shapeBorder = atoi(properties["ShapeBorder"].c_str());
        
		b2PolygonShape shape;
		b2FixtureDef fixture;
		b2CircleShape circle;
		setFixtureDefPropertiesFromMap(properties, &fixture);
		
		if((bool)atoi(properties["IsCircle"].c_str()))
		{
			circle.m_radius = (size.width*scale.x - shapeBorder)/2/PTM_RATIO;
			fixture.shape = &circle;
		}
		else
		{
			shape.SetAsBox((size.width*scale.x - shapeBorder)/PTM_RATIO/2, 
						   (size.height*scale.y - shapeBorder)/PTM_RATIO/2);
			
			fixture.shape = &shape;
		}
		
		body->CreateFixture(&fixture);
	}
	else
	{
		for(size_t i = 0; i < fixtures.size(); ++i)
		{
			std::vector<std::string>& curFixture(fixtures[i]);
			int size = (int)curFixture.size();
			b2Vec2 *verts = new b2Vec2[size];
			b2PolygonShape shape;
			int k = 0;
			
			for(int j = 0; j < size; ++j)
			{ 
				std::string pointStr = curFixture[j];
				CCPoint point = cocos2d::CCPointFromString(pointStr.c_str());
				verts[k] = b2Vec2(point.x*(scale.x)/PTM_RATIO, 
								  point.y*(scale.y)/PTM_RATIO);
				++k;
			}
			shape.Set(verts, size);		
			b2FixtureDef fixture;
			setFixtureDefPropertiesFromMap(properties, &fixture);
			fixture.shape = &shape;
			body->CreateFixture(&fixture);
			delete verts;
		}
	}
	
	return body;	
}
////////////////////////////////////////////////////////////////////////////////

SpriteInfo SH_currentSpriteInfo;
bool SH_readSprites = false;

std::vector<std::vector<std::string> >  SH_currentPolygonInfo;
std::vector<std::string> SH_currentFixtureInfo;
bool SH_readFixture = false;

std::string SH_currentKey;

int SH_dict = 0;
int SH_array = 0;


void SpriteHelperLoader::key(const std::string& arg)
{
	SH_currentKey = arg;
	if(std::string::npos != arg.find("SPRITES_INFO")){
		SH_readSprites = true;
	}
	else if(std::string::npos != arg.find("Fixtures")){
		SH_readFixture = true;
	}
}

void SpriteHelperLoader::pushValue(const std::string& arg)
{
	if(SH_readSprites){
		SH_currentSpriteInfo.properties[SH_currentKey] = arg;
	}
	if(SH_readFixture){
		SH_currentFixtureInfo.push_back(arg);
	}
}


void SpriteHelperLoader::arrayBegin(){
   
	SH_array += 1;
}

void SpriteHelperLoader::noArray(){
    
    if(SH_readFixture){
        SH_readFixture = false;
        SH_currentFixtureInfo.clear();
        SH_currentPolygonInfo.clear();
    }
}
void SpriteHelperLoader::arrayEnd(){
	SH_array -= 1;

	if(0 == SH_array){
		if(SH_readSprites){
			SH_readSprites = false;
		}		
	}

	if(SH_readFixture){
		if(2 == SH_array){
			SH_currentPolygonInfo.push_back(SH_currentFixtureInfo);
			SH_currentFixtureInfo.clear();
		}
		else if(1 == SH_array){
			SH_currentSpriteInfo.fixtures = SH_currentPolygonInfo;
			SH_readFixture = false;
			SH_currentPolygonInfo.clear();
		}
	}
}

void SpriteHelperLoader::dictBegin(){
	SH_dict += 1;
}
void SpriteHelperLoader::dictEnd(){
	SH_dict -= 1;

	if(1 == SH_dict){
		if(SH_readSprites){
			lhSprites.push_back(new SpriteInfo);
			lhSprites[lhSprites.size()-1]->properties = SH_currentSpriteInfo.properties;
			lhSprites[lhSprites.size()-1]->fixtures = SH_currentSpriteInfo.fixtures;
            SH_currentSpriteInfo.properties.clear();
            SH_currentSpriteInfo.fixtures.clear();
		}
	}
}

std::string SpriteHelperLoader::valueForField(const std::string& field)
{
	std::size_t posStart = field.find_first_of(">");
	std::size_t posEnd = field.find_last_of("<");
	return field.substr(posStart+1, posEnd-posStart-1);
}
////////////////////////////////////////////////////////////////////////////////////
void SpriteHelperLoader::loadSpriteHelperSceneFile(const std::string& levelFile)
{
	const char* path = CCFileUtils::fullPathFromRelativePath(levelFile.c_str());
	
	assertMessage(path != 0, "Invalid level file. Please add the LevelHelper scene file to Resource folder.");
	
	std::ifstream infile;
	infile.open (path);
	
	assertMessage(infile.is_open(), "Invalid level file. Please add the LevelHelper scene file to Resource folder.");
	
	
	while(!infile.eof())
	{
		std::string line;
		getline(infile,line);
		
		if (std::string::npos != line.find("<key>")){
			key(valueForField(line).c_str());
			
		}
		else if (std::string::npos != line.find("<string>")){
			pushValue(valueForField(line).c_str());
		}
		else if (std::string::npos != line.find("<real>")){
			pushValue(valueForField(line).c_str());
		}
		else if (std::string::npos != line.find("<integer>")){
			pushValue(valueForField(line).c_str());
		}
		else if (std::string::npos != line.find("<true/>")){
			pushValue("1");
		}
		else if (std::string::npos != line.find("<false/>")){
			pushValue("0");
		}
		else if (std::string::npos != line.find("<dict>")){
			dictBegin();
		}
		else if (std::string::npos != line.find("</dict>")){
			dictEnd();
		}
		else if (std::string::npos != line.find("<array>")){
			arrayBegin();
		}
		else if (std::string::npos != line.find("</array>")){
			arrayEnd();
		}
		else if (std::string::npos != line.find("<array/>")){
			noArray();
		}
	}
	infile.close();
	
	
	SH_readSprites = false;
	SH_currentPolygonInfo.clear();
	SH_currentFixtureInfo.clear();
	SH_readFixture = false;
	SH_currentKey = "";	
	SH_dict = 0;
	SH_array = 0;
	
	
	///////////////////DEBUG////////////////////////////////
	/*
	for(size_t i = 0; i < lhSprites.size(); ++i)
	{
		SpriteInfo* sprite = lhSprites[i];
		printf("-----------------SPRITE-------------------------------\n");
		std::map<std::string, std::string>::iterator It;
		for (It = sprite->properties.begin() ; It != sprite->properties.end(); It++ )
		{
			printf("key %s = %s\n", (*It).first.c_str(), (*It).second.c_str());
		}
		
        for(int j = 0; j < sprite->fixtures.size(); ++j)
        {
            std::vector<std::string> fix = sprite->fixtures[j];
            
            printf("FIXTURE\n");
            for(int k = 0; k < fix.size(); ++k)
            {
                printf("pt: %s\n", fix[k].c_str());
            }
        }
        
		printf("-----------------------------------------------------\n");
	}*/
}


/// custom marius function

std::vector<std::string> SpriteHelperLoader::frameListWithAnimName( const std::string& name )
{
	std::vector<std::string> frameNames;

	bool _continue = true;
	int i = 1;
    while( _continue )
    {
        std::string frame(concatenate(name, i));
        
		LHDictionary* spriteProperties = propertyForName( frame );

		if( spriteProperties != NULL )
			frameNames.push_back(frame);
		else
			_continue = false;

		i++;
    }

    return frameNames;
}


CCAction* SpriteHelperLoader::actionForAnimName(const std::string& name, const float& delay)
{
	return actionForFrames( frameListWithAnimName(name), delay );
}